#pragma once

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>

using namespace std;

template <typename T>
class BlockQueue
{
public:
    BlockQueue(int _max=10)
    :maxcap(_max)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&p_cond,nullptr);
        pthread_cond_init(&c_cond,nullptr);
    }

    void push(T t)
    {
        pthread_mutex_lock(&_mutex);
        while(q.size()==maxcap)   
        {
            pthread_cond_wait(&p_cond,&_mutex);
        }
        
        q.push(t);
        pthread_cond_signal(&c_cond);
        pthread_mutex_unlock(&_mutex);
    }

    T pop()
    {
        pthread_mutex_lock(&_mutex);
        while(q.size()==0)
        {
            pthread_cond_wait(&c_cond,&_mutex);
        }

        T t=q.front();
        q.pop();

        pthread_cond_signal(&c_cond);
        pthread_mutex_unlock(&_mutex);
        return t;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }

private:
    pthread_cond_t c_cond;//consume
    pthread_cond_t p_cond;//produce
    pthread_mutex_t  _mutex;//
    int maxcap;
    queue<T> q;
};